package pl.enigmatic.visual.util.math;

import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

import pl.enigmatic.input.DoubleInput;
import pl.enigmatic.visual.DataVisualizer;


public abstract class MatrixVisualizerBase<M> extends DataVisualizer<M> {

	/** */
	private static final long serialVersionUID = 1L;

	private JLabel[][] labels;
	private Input[][] inputs;

	public MatrixVisualizerBase(final M x) {
		super(x);
	}

	public MatrixVisualizerBase(final Object source, final String property) throws Exception {
		super(source, property);
	}

	protected abstract int getRows();

	protected abstract int getCols();

	protected abstract double get(M m, int row, int col);

	protected abstract void set(M m, int row, int col, double val);

	@Override
	protected Component createVisualizer() {
		labels = new JLabel[getRows()][getCols()];
		final JPanel panel = new JPanel(new GridLayout(getRows(), getCols(), 5, 5));
		for (int i = 0; i < labels.length; i++) {
			for (int j = 0; j < labels[i].length; j++) {
				labels[i][j] = new JLabel();
				labels[i][j].setVerticalAlignment(SwingConstants.CENTER);
				labels[i][j].setHorizontalAlignment(SwingConstants.CENTER);
				panel.add(labels[i][j]);
			}
		}
		return panel;
	}

	@Override
	protected void updateVisualizer(final M data) {
		for (int i = 0; i < labels.length; i++) {
			for (int j = 0; j < labels[i].length; j++) {
				labels[i][j].setText(Double.toString(get(data, i, j)));
			}
		}
	}

	@Override
	protected Component createSetter() {
		inputs = new Input[getRows()][getCols()];
		final JPanel panel = new JPanel(new GridLayout(getRows(), getCols(), 5, 5));
		for (int i = 0; i < labels.length; i++) {
			for (int j = 0; j < labels[i].length; j++) {
				inputs[i][j] = new Input(i, j) {

					/** */
					private static final long serialVersionUID = 1L;

					@Override
					protected void onValueChanged(final double old) {
						set(get(), getRow(), getColumn(), (float) getValue());
						MatrixVisualizerBase.this.update();
					}
				};
				panel.add(inputs[i][j]);
			}
		}
		return panel;
	}

	@Override
	protected void updateSetter(final M data) {
		for (int i = 0; i < labels.length; i++) {
			for (int j = 0; j < labels[i].length; j++) {
				inputs[i][j].setValue(get(data, i, j));
			}
		}
	}

	protected static class Input extends DoubleInput {

		/** */
		private static final long serialVersionUID = 1L;
		private final int row;
		private final int column;

		public Input(final int row, final int column) {
			this.row = row;
			this.column = column;
			addFocusListener(new FocusListener() {

				@Override
				public void focusLost(FocusEvent e) {}

				@Override
				public void focusGained(FocusEvent e) {
					setSelectionStart(0);
					setSelectionEnd(getText().length());
				}
			});
		}

		public int getRow() {
			return row;
		}

		public int getColumn() {
			return column;
		}
	}

}
